package com.baidu.iit.pvm;

import com.baidu.iit.pvm.delegate.ActivityBehavior;
import com.baidu.iit.pvm.delegate.ExecutionListener;
import com.baidu.iit.pvm.process.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * 流程构建类通过这里类来构建流程，可以根据不同的需要，建立不同的流程描述构造类
 *
 * 该类是property类，每次都要创建新的
 *
 * @NotThreadSafe
 *
 * Created by 卫立 on 2014/4/7.
 */
public class ProcessDefinitionBuilder {

    //流程定义
    protected ProcessDefinitionImpl processDefinition;

    //流程中的节点
    protected Stack<ScopeImpl> scopeStack = new Stack<ScopeImpl>();


    //指向当前的操作元素
    protected ProcessElementImpl processElement;


    //当前添加的路程路径
    protected TransitionImpl transition;

    //未进行配对的流程路径
    protected List<Object[]> unresolvedTransitions = new ArrayList<Object[]>();


    //构造函数定义一个名称为空的流程
    public ProcessDefinitionBuilder() {
        this(null);
    }

    /**
     * 根据Id定义一个流程实例
     * @param processDefinitionId
     */
    public ProcessDefinitionBuilder(String processDefinitionId) {
        processDefinition = new ProcessDefinitionImpl(processDefinitionId);
        this.processElement=processDefinition; //定义关联
        scopeStack.push(processDefinition);
    }

    /**
     * 通过流程定义创建节点，当前的堆栈的顶点是ProcessDefintion 对象
     * @param id
     * @return
     */
    public ProcessDefinitionBuilder createActivity(String id) {
        //从堆栈中获取 ProcessDefintion
        ActivityImpl activity = scopeStack.peek().createActivity(id);

        //把当前的节点压入堆栈
        scopeStack.push(activity);

        //当前的配置元素设置成 新建的节点
        processElement = activity;

        //跳转路径必须设置唯恐，要不影响监听事件的添加
        transition = null;

        return this;
    }

    /**
     * 结束节点的配置
     * @return
     */
    public ProcessDefinitionBuilder endActivity() {

        //把当前的节点出栈
        scopeStack.pop();
        //把processDefinition 对象设置为当前的执行对象
        processElement = scopeStack.peek();
        //清空当前的路径，否则影响后续的事件监听
        transition = null;

        return this;
    }

    /**
     * 设置流程的起点
     * 该函数的调用必须在创建了节点后调用
     * @return
     */
    public ProcessDefinitionBuilder initial() {
        //设置当前栈顶的节点为流程的起始节点
        processDefinition.setInitial(getActivity());
        return this;
    }

    /**
     * 创建节点的出路径
     * @param destinationActivityId
     * @return
     */
    public ProcessDefinitionBuilder startTransition(String destinationActivityId) {
        return startTransition(destinationActivityId, null);
    }

    public ProcessDefinitionBuilder startTransition(String destinationActivityId, String transitionId) {
        if (destinationActivityId==null) {
            throw new PvmException("destinationActivityId 为空!");
        }
        //此时的栈顶必须是Activity,不能是processDefinition
        ActivityImpl activity = getActivity();

        //通过节点创建该节点的出路径
        transition = activity.createOutgoingTransition(transitionId);

        //由于没有指定目标节点，所以保存路径到未处理队列中
        unresolvedTransitions.add(new Object[]{transition, destinationActivityId});

        //设置当前的处理元素为 路径
        processElement = transition;

        return this;
    }

    /**
     * 结束路径的配置和starTransition 是成对出现的
     * @return
     */
    public ProcessDefinitionBuilder endTransition() {

        processElement = scopeStack.peek();
        transition = null;
        return this;
    }


    /**
     * 路径定义是对 startTransition 和 endTranstion 的封装处理
     * 这个函数适合在路径上没有监听事件的情况下使用
     * @param destinationActivityId
     * @return
     */
    public ProcessDefinitionBuilder transition(String destinationActivityId) {
        return transition(destinationActivityId, null);
    }

    /**
     * 路径定义是对 startTransition 和 endTranstion 的封装处理
     * 这个函数适合在路径上没有监听事件的情况下使用
     * @param destinationActivityId
     * @param transitionId
     * @return
     */
    public ProcessDefinitionBuilder transition(String destinationActivityId, String transitionId) {
        startTransition(destinationActivityId, transitionId);
        endTransition();
        return this;
    }

    /**
     * 设置节点上的动作
     * 该函数的操作必须在startActivity和 endActivity 之间执行
     * @param activityBehaviour
     * @return
     */
    public ProcessDefinitionBuilder behavior(ActivityBehavior activityBehaviour) {
        getActivity().setActivityBehavior(activityBehaviour);
        return this;
    }


    /**
     * 对当前的执行元素进行属性设置，该函数在所有的 start 和 end 之间进行操作
     * @param name
     * @param value
     * @return
     */
    public ProcessDefinitionBuilder property(String name, Object value) {
        processElement.setProperty(name, value);
        return this;
    }

    /**
     * 构建流程实例
     * @return
     */
    public PvmProcessDefinition buildProcessDefinition() {
        for (Object[] unresolvedTransition: unresolvedTransitions) {
            TransitionImpl transition = (TransitionImpl) unresolvedTransition[0];
            String destinationActivityName = (String) unresolvedTransition[1];
            ActivityImpl destination = processDefinition.findActivity(destinationActivityName);
            if (destination == null) {
                throw new RuntimeException("目标节点 '"+destinationActivityName+"' 没有找到.  (引用的路径ID为 '"+transition.getSource().getId()+"')");
            }
            transition.setDestination(destination);
        }
        return processDefinition;
    }

    /**
     * 从堆栈中获取最后添加的节点
     * @return
     */
    protected ActivityImpl getActivity() {
        return (ActivityImpl) scopeStack.peek();
    }

    /**
     * 设置当前的节点是个单元节点，将包括其他的节点
     * @return
     */
    public ProcessDefinitionBuilder scope() {
        getActivity().setScope(true);
        return this;
    }

    /**
     * 在跳转路径上添加监听事件
     * @param executionListener
     * @return
     */
    public ProcessDefinitionBuilder executionListener(ExecutionListener executionListener) {
        if (transition!=null) {
            transition.addExecutionListener(executionListener);
        } else {
            throw new PvmException("只有在跳转路径上才能添加监听器!");
        }
        return this;
    }

    /**
     * 添加流程节点上的监听事件
     * @param eventName  事件名称可以从常量中获取
     * @param executionListener  具体的监听任务
     * @return
     */
    public ProcessDefinitionBuilder executionListener(String eventName, ExecutionListener executionListener) {
        if (transition==null) {
            scopeStack.peek().addExecutionListener(eventName, executionListener);
        } else {
            throw new PvmException("只有在流程节点和流程上才能添加监听器");
        }
        return this;
    }
}
